#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include "Server.h"
#include "WorkSvr.h"
#include "MemPool.h"
#include "SvrMgr.h"

CServer::CServer()
{
    m_ilfd = -1;
    m_iefd = -1;
    m_irspfd = -1;
    m_iwspfd = -1;
    m_bRunning = true;
    m_pEvnts = nullptr;
}

CServer::~CServer()
{
    Destroy();
}

bool CServer::Init(int ilfd)
{
    bool bRet;
    int fds[2] = {-1};

    if (0 >= ilfd)
        return false;

    if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
        return false;

    m_ilfd = ilfd;
    m_irspfd = fds[0];
    m_iwspfd = fds[1];

    // 创建epoll
    m_iefd = epoll_create1(0);

    if (-1 == m_iefd)
    {
        Destroy();
        return false;
    }

    // 绑定m_ilfd到epoll,ev.data.ptr 为 m_ilfd 地址
    bRet = UpdateEvents(m_ilfd, EPOLLIN | EPOLLET | EPOLLEXCLUSIVE, EPOLL_CTL_ADD, &m_ilfd);

    if (!bRet)
    {
        Destroy();
        printf("Error epoll_ctl:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
    }

    bRet = SetNonBlock(m_irspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    bRet = UpdateEvents(m_irspfd, EPOLLIN | EPOLLET, EPOLL_CTL_ADD, &m_irspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    bRet = SetNonBlock(m_iwspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    bRet = UpdateEvents(m_iwspfd, EPOLLOUT | EPOLLET, EPOLL_CTL_ADD, &m_iwspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    m_pEvnts = (epoll_event *)calloc(MAXEVENTS, sizeof(epoll_event));

    if (nullptr == m_pEvnts)
    {
        Destroy();
        printf("Error (nullptr == m_pEvnts):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    return true;
}

bool CServer::Destroy()
{
    if (m_bRunning)
        Stop();

    if (-1 != m_iefd)
    {
        close(m_iefd);
        m_iefd = -1;
    }

    if (-1 != m_iwspfd)
    {
        close(m_iwspfd);
        m_iwspfd = -1;
    }

    if (-1 != m_irspfd)
    {
        close(m_irspfd);
        m_irspfd = -1;
    }

    if (nullptr != m_pEvnts)
    {
        free(m_pEvnts);
        m_pEvnts = nullptr;
    }

    return true;
}

bool CServer::Start()
{
    bool bRet;

    while (m_bRunning)
    {
        bRet = EventHandle();

        if (!bRet)
            break;
    }

    return true;
}

bool CServer::Stop()
{
    m_bRunning = false;

    if (-1 != m_ilfd)
        UpdateEvents(m_ilfd, 0, EPOLL_CTL_ADD, nullptr);

    if (-1 != m_irspfd)
        UpdateEvents(m_irspfd, 0, EPOLL_CTL_DEL, nullptr);

    if (-1 != m_iwspfd)
        UpdateEvents(m_iwspfd, 0, EPOLL_CTL_DEL, nullptr);

    return true;
}

bool CServer::EventHandle()
{
    bool bRet = true;
    int iLoop;
    int iCount = epoll_wait(m_iefd, m_pEvnts, MAXEVENTS, -1);

    // 被中断
    if ((-1 == iCount) && (errno == EINTR))
    {
        // printf("Log:\t(errno == EINTR):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return true;
    }

    for (iLoop = 0; iLoop < iCount; iLoop++)
    {
        epoll_event &ee = m_pEvnts[iLoop];

        if ((ee.events & EPOLLERR) || (ee.events & EPOLLHUP))
        {
            bRet = ErrorEvent(ee);

            if (!bRet)
            {
                printf("Error ErrorEvent:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                break;
            }
        }
        else if (ee.events & EPOLLIN)
        {
            bRet = InputEvent(ee);

            if (!bRet)
            {
                printf("Error InputEvent:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                break;
            }
        }
        else if (ee.events & EPOLLOUT)
        {
            bRet = OutputEvent(ee);

            if (!bRet)
            {
                printf("Error OutputEvent:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                break;
            }
        }
    }

    return bRet;
}

bool CServer::SetNonBlock(int ifd)
{
    int flags;
    int result;

    flags = fcntl(ifd, F_GETFL, 0);

    if (-1 == flags)
    {
        printf("Error fcntl F_GETFL:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    flags |= O_NONBLOCK;
    result = fcntl(ifd, F_SETFL, flags);

    if (-1 == result)
    {
        printf("Error fcntl F_SETFL:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    return true;
}

bool CServer::UpdateEvents(int ifd, int iEvnts, int iOpt, void *pData)
{
    bool bRet = true;
    struct epoll_event ev;

    if (-1 == m_iefd)
        return false;

    memset(&ev, 0, sizeof(ev));
    ev.events = iEvnts;
    ev.data.ptr = pData;
    int s = epoll_ctl(m_iefd, iOpt, ifd, &ev);

    if (-1 == s)
        bRet = false;

    return bRet;
}

int CServer::BindAndListen(unsigned short nPort)
{
    struct addrinfo aiConfig;
    struct addrinfo *pResult = nullptr;
    struct addrinfo *pItem = nullptr;
    int iRetVal;
    int isfd = -1;

    memset(&aiConfig, 0, sizeof(struct addrinfo));
    aiConfig.ai_family = AF_UNSPEC;
    aiConfig.ai_socktype = SOCK_STREAM;
    aiConfig.ai_flags = AI_PASSIVE;

    char szBufferPort[24] = {0};
    sprintf(szBufferPort, "%d", nPort);
    iRetVal = getaddrinfo(nullptr, szBufferPort, &aiConfig, &pResult);

    if (0 != iRetVal)
    {
        printf("Error getaddrinfo:%s\tfile=%s\tline=%d\n", gai_strerror(iRetVal), __FILE__, __LINE__);
        return -1;
    }

    for (pItem = pResult; pItem != nullptr; pItem = pItem->ai_next)
    {
        isfd = socket(pItem->ai_family, pItem->ai_socktype, pItem->ai_protocol);

        if (-1 == isfd)
            continue;

        iRetVal = bind(isfd, pItem->ai_addr, pItem->ai_addrlen);

        if (0 == iRetVal)
            break;

        close(isfd);
    }

    if (nullptr == pItem)
    {
        freeaddrinfo(pResult);
        printf("Error bind:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return -1;
    }

    freeaddrinfo(pResult);

    if (!SetNonBlock(isfd))
    {
        close(isfd);
        printf("Error (-1 == iRetVal):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return -1;
    }

    iRetVal = listen(isfd, SOMAXCONN);

    if (-1 == iRetVal)
    {
        close(isfd);
        printf("Error listen:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return -1;
    }

    return isfd;
}

// bool CServer::IPCWrite(void *pSrc, int iLen)
// {
//     int size = write(m_iwspfd, pSrc, iLen);
// }

// bool CServer::IPCRead(void *pDest, int iLen)
// {
//     read(m_irspfd, pDest, iLen);
// }